package com.shop.tools;

import com.shop.entity.Order;

import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.*;
import java.util.*;

/**
 * com.Dept.Tools文档说明
 *
 * @author Angluzha
 * @version 2022/9/2
 */
public class JdbcTool {
    /**
     * 线程安全Connection对象
     */
    private static final ThreadLocal<Connection> CONNECTION_T = new ThreadLocal<>();
    /**
     * 简简单单Properties JDBC配置文件加载
     */
    private static final Properties P = new Properties();
    /**
     * 线程安全的 是否在运行事务 Flag
     */
    private static final ThreadLocal<Boolean> IS_FXS =new ThreadLocal<>();

    /**
     * 在类加载时加载Jdbc的Properties配置文件
     */
    static{
        try {
            P.load(JdbcTool.class.getClassLoader().getResourceAsStream("jdbc.properties"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取当前线程的Connection连接对象
     * 一般在类内调用
     * 公开为可能的外部操作提供连接对象
     * @return 当前线程的Connection连接对象
     */
    public static Connection getConnection(){
        //从线程变量池中取出一个Connection对象
        //若为Null 则创建一个Connection对象置于线程变量池中
        Connection connection = CONNECTION_T.get();
        try {
            if (connection==null){
                Class.forName(P.getProperty("driverClassName"));
                connection = DriverManager.getConnection(P.getProperty("url"), P.getProperty("username"), P.getProperty("password"));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        CONNECTION_T.set(connection);

        //为当前线程创建一个(事务Flag)
        Boolean isFX = IS_FXS.get();
        if (isFX==null){
            Boolean aBoolean = true;
            IS_FXS.set(aBoolean);
        }
        return connection;
    }

    /**
     * 开启事务
     * @throws SQLException
     */
    public static void begin() throws SQLException {
        //关闭当前线程Connection的自动事务
        getConnection().setAutoCommit(false);
        //将(事务Flag)置为False
        Boolean isFx = false;
        IS_FXS.set(isFx);
    }

    /**
     * 提交事务
     * @throws SQLException
     */
    public static void commit() throws SQLException {
        //提交当前事务 并将(事务Flag)置为true
        getConnection().commit();
        Boolean isFx = true;
        IS_FXS.set(isFx);
    }
    /***
     * 回滚事务
     */
    public static void rollback(){
        //回滚当前事务 并将(事务Flag)置为true
        //回滚出异常实为天命难违，故不做抛出处理
        try {
            getConnection().rollback();
            Boolean isFx = true;
            IS_FXS.set(isFx);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过数据库链接对象 执行预处理sql语句加载
     * @param sql 执行所需要的sql语句
     * @param objects sql语句中占位符所需的参数
     * @return sql预处理执行对象
     * @throws SQLException
     */
    private static PreparedStatement toThree(String sql,Object...objects) throws SQLException {
        PreparedStatement statement = getConnection().prepareStatement(sql);
        for (int i = 0; i < objects.length; i++) {
            statement.setObject(i+1,objects[i]);
        }
        return statement;
    }

    /**
     * 通过类的反射对象 执行对象创建，属性获权，属性按名称反射加载操作
     * 并在每一个对象加载完毕后添加到泛型集合中
     * @param clazz   sql结果集实体类的Class对象
     * @param sql     待执行的sql语句
     * @param objects sql语句占位符所需的参数
     * @return        实体类集合
     * @param <T>     实体类类型
     * @throws SQLException
     */
    public static <T> List<T> selectList(Class<T> clazz, String sql, Object...objects) throws SQLException {
        //获取sql预处理执行对象并执行，得到结果集rs
        ResultSet rs = toThree(sql,objects).executeQuery();
        //创建实体类集合
        List<T> list = new ArrayList<>();

        try{
            //加载orm映射文件对象
            Properties orm = new Properties();
            orm.load(JdbcTool.class.getResourceAsStream("/orm.properties"));
            //获取实体类中的属性
            Field[] fields = clazz.getDeclaredFields();
            //将属性数组fields中的所有属性权限设置为public
            Arrays.stream(fields).forEach(e->e.setAccessible(true));
            //按行读取结果集rs
            while(rs.next()){
                //通过实体类Class对象创建实体类
                T t = clazz.getDeclaredConstructor().newInstance();
                for (Field field:fields) {
                    //按名获取结果
                    String name = field.getName();
                    Object value =null;
                    try {
                        value = rs.getObject(name);
                    }catch (SQLException e){
                        //如果按名称获取值失败 则从orm映射文件中获取别名
                        String ormName = orm.getProperty(name);
                        if (ormName!=null&&!"".equals(ormName)){
                            value = rs.getObject(ormName);
                        }
                    }
                    //将结果置于对象
                    field.set(t,value);
                }
                //将对象添加到泛型集合
                list.add(t);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        //自动关闭链接对象方法
        autoFx();
        return list;
    }

    /**
     * 查寻符合条件的实体类构成泛型集合 并返回泛型集合中的第一个元素
     * @param clazz     实体类Class对象
     * @param sql       待执行的sql语句
     * @param objects   占位符待传参数
     * @return          符合条件的第一个实体类
     * @param <T>       实体类
     * @throws SQLException
     */
    public static <T> T selectOne(Class<T> clazz,String sql,Object...objects) throws SQLException {
        List<T> list = selectList(clazz, sql, objects);
        autoFx();
        for (T t:list) {
            return t;
        }
        return null;
    }

    /**
     * update，insert，delete公用方法
     * @param sql       待执行sql语句
     * @param objects   占位符所需参数
     * @return          受影响的数据库行数
     */
    public static int write(String sql,Object...objects){
        try {
            PreparedStatement three = toThree(sql, objects);
            return three.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
            return 0;
        }finally {
            autoFx();
        }
    }

    /**
     * 事务Flag
     * IS_FXS.get()==true 自动事务开启 清除当前线程中的Connection对象
     * IS_FXS.get()==False 自动事务关闭 每次执行不清空Connection对象
     */
    private static void autoFx(){
        if (IS_FXS.get()){
            CONNECTION_T.remove();
        }
    }
    public static <T> T selectResult(Class<T> clazz, String sql, Object...objects) throws SQLException {
        //获取sql预处理执行对象并执行，得到结果集rs
        ResultSet rs = toThree(sql,objects).executeQuery();
        T t = null;
        if (rs.next()){
             t = rs.getObject(1, clazz);
        }
        //自动关闭链接对象方法
        autoFx();
        return t;
    }


    public static Integer insertGet(String sql, Object...objects) throws SQLException {
        PreparedStatement statement = getConnection().prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
        for (int i = 0; i < objects.length; i++) {
            statement.setObject(i+1,objects[i]);
        }
        int num = statement.executeUpdate();

        ResultSet rs = statement.getGeneratedKeys();
        autoFx();
        while (rs.next()){
            return rs.getInt(1);
        }
        return null;
    }
}
